package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.user.UserInfo;
import com.atguigu.tingshu.model.user.UserPaidAlbum;
import com.atguigu.tingshu.model.user.UserPaidTrack;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.pattern.DeliveryStrategy;
import com.atguigu.tingshu.user.pattern.factory.DeliveryStrategyFactory;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

	@Autowired
	private UserInfoMapper userInfoMapper;

	@Autowired
	private WxMaService wxMaService;

	@Autowired
	RabbitService rabbitService;

	@Autowired
	private RedisTemplate redisTemplate;

	/**
	 * 微信一键登录
	 *
	 * @param code 小程序端根据当前微信，生成访问为微信服务端临时凭据
	 * @return
	 */
	@Override
	public Map<String, String> wxLogin(String code) {
		try {
			//1.拿着临时凭据+应用ID+应用秘钥 调用微信接口 获取当前微信账户唯一标识：openId
			//1.1 微信账户信息业务类
			WxMaUserService userService = wxMaService.getUserService();
			//1.2 获取会话信息
			WxMaJscode2SessionResult sessionInfo = userService.getSessionInfo(code);
			//1.3 获取微信账号唯一标识
			String openid = sessionInfo.getOpenid();

			//2.根据微信账户唯一标识，查询数据库，看当前微信是否已经注册
			UserInfo userInfo = userInfoMapper.selectOne(
					new LambdaQueryWrapper<UserInfo>()
							.eq(UserInfo::getWxOpenId, openid)
			);

			//3.如果微信账户首次注册，则新增用户记录，为用户初始化账户记录用于后续订单支付
			if (userInfo == null) {
				//3.1 新增用户记录 绑定微信账户唯一标识
				userInfo = new UserInfo();
				userInfo.setWxOpenId(openid);
				userInfo.setNickname(IdUtil.nanoId());
				userInfo.setAvatarUrl("http://192.168.200.6:9000/tingshu/2024-04-02/0b033705-4603-4fb2-bd0f-db84076aef84.jpg");
				userInfoMapper.insert(userInfo);
				//3.2 TODO 为当前注册用户初始化账户记录
				// 方案一：Openfeign远程调用 分布式事务问题  方案二：采用MQ可靠性消息队列实现数据最终一致
				//3.2.1 构建消息对象 注意：如果是VO对象一定要实现序列化接口以及生成序列化版本号
				Map<String, Object> map = new HashMap<>();
				map.put("userId", userInfo.getId());
				map.put("amount", new BigDecimal("100"));
				map.put("title", "新用户专项体验金活动");
				map.put("orderNo", "ZS"+IdUtil.getSnowflakeNextId());
				//3.2.2 发送消息到MQ
				rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, map);
			}

			//4.基于当前用户信息生成令牌
			//4.1 创建令牌
			String token = IdUtil.randomUUID();
			//4.2 构建登录成功后Redis的Key 形式为：user:login:token
			String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;
			//4.3 构建登录成功后Redis的Value 形式为：userInfo
			UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
			//4.4 存入Redis 设置有效期：7天
			redisTemplate.opsForValue().set(loginKey, userInfoVo, RedisConstant.USER_LOGIN_KEY_TIMEOUT, TimeUnit.SECONDS);

			//5.封装令牌返回给前端
			Map<String, String> map = new HashMap<>();
			map.put("token", token);
			return map;
		} catch (WxErrorException e) {
			log.error("微信登录失败");
			throw new GuiguException(500, "微信登录失败");
		}
	}


	/**
	 * 根据用户ID查询用户基本信息
	 *
	 * @param userId
	 * @return
	 */
	@Override
	public UserInfoVo getUserInfo(Long userId) {
		UserInfo userInfo = userInfoMapper.selectById(userId);
		if (userInfo != null) {
			return BeanUtil.copyProperties(userInfo, UserInfoVo.class);
		}
		return null;
	}


	/**
	 * 更新用户信息方法
	 * 只允许修改昵称头像
	 * @param userInfoVo
	 */
	@Override
	public void updateUser(UserInfoVo userInfoVo) {
		//1.获取用户ID
		Long userId = AuthContextHolder.getUserId();
		//2.更新用户昵称跟头像
		UserInfo userInfo = new UserInfo();
		userInfo.setId(userId);
		userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());
		userInfo.setNickname(userInfoVo.getNickname());
		userInfoMapper.updateById(userInfo);
	}


	@Autowired
	private UserPaidAlbumMapper userPaidAlbumMapper;

	@Autowired
	private UserPaidTrackMapper userPaidTrackMapper;

	/**
	 * 提交需要检查购买状态声音ID列表，响应每个声音购买状态
	 *
	 * @param userId                        用户ID
	 * @param albumId                       专辑ID
	 * @param needCheckPayStatusTrackIdList 待检查购买状态声音ID列表
	 * @return
	 */
	@Override
	public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStatusTrackIdList) {
		Map<Long, Integer> map = new HashMap<>();
		//1.根据用户ID+专辑ID查询专辑购买记录
		Long count = userPaidAlbumMapper.selectCount(
				new LambdaQueryWrapper<UserPaidAlbum>()
						.eq(UserPaidAlbum::getUserId, userId)
						.eq(UserPaidAlbum::getAlbumId, albumId)
		);

		//2. 如果已购买专辑，将所有待检查购买状态声音 购买状态设置为 1 响应
		if (count > 0) {
			for (Long trackId : needCheckPayStatusTrackIdList) {
				map.put(trackId, 1);
			}
			return map;
		}

		//3. 根据用户ID+专辑ID查询已购声音记录
		List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
				new LambdaQueryWrapper<UserPaidTrack>()
						.eq(UserPaidTrack::getUserId, userId)
						.eq(UserPaidTrack::getAlbumId, albumId)
						.select(UserPaidTrack::getTrackId)
		);

		//4. 如果不存再已购声音，将所有待检查购买状态声音 购买状态设置为 0 响应
		if (CollUtil.isEmpty(userPaidTrackList)) {
			for (Long trackId : needCheckPayStatusTrackIdList) {
				map.put(trackId, 0);
			}
			return map;
		}


		//5.如果存在已购声音，将提交检查声音ID列表中，已购声购买状态设置为：1。未购买设置为0
		List<Long> userPaidTrackIdList = userPaidTrackList.stream().map(UserPaidTrack::getTrackId).collect(Collectors.toList());
		for (Long trackId : needCheckPayStatusTrackIdList) {
			if (userPaidTrackIdList.contains(trackId)) {
				map.put(trackId, 1);
			} else {
				map.put(trackId, 0);
			}
		}
		return map;
	}

	/**
	 * 判断当前用户是否购买过指定专辑
	 *
	 * @param albumId
	 * @return
	 */
	@Override
	public Boolean isPaidAlbum(Long userId, Long albumId) {
		Long count = userPaidAlbumMapper.selectCount(
				new LambdaQueryWrapper<UserPaidAlbum>()
						.eq(UserPaidAlbum::getUserId, userId)
						.eq(UserPaidAlbum::getAlbumId, albumId)
		);
		return count > 0;
	}

	/**
	 * 查询当前用户某个专辑下已购声音ID列表
	 *
	 * @param albumId
	 * @return
	 */
	@Override
	public List<Long> findUserPaidTrackList(Long userId, Long albumId) {
		List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(
				new LambdaQueryWrapper<UserPaidTrack>()
						.eq(UserPaidTrack::getUserId, userId)
						.eq(UserPaidTrack::getAlbumId, albumId)
						.select(UserPaidTrack::getTrackId)
		);
		if (CollUtil.isNotEmpty(userPaidTrackList)) {
			List<Long> userPaidTrackIdList = userPaidTrackList.stream()
					.map(UserPaidTrack::getTrackId)
					.collect(Collectors.toList());
			return userPaidTrackIdList;
		}
		return null;
	}


	@Autowired
	private AlbumFeignClient albumFeignClient;

	@Autowired
	private UserVipServiceMapper userVipServiceMapper;

	@Autowired
	private VipServiceConfigMapper vipServiceConfigMapper;

	@Autowired
	private DeliveryStrategyFactory deliveryStrategyFactory;

	/**
	 * 用户支付成功后，虚拟物品发货
	 *
	 * @param userPaidRecordVo
	 * @return
	 */
	@Override
	public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {
		//TODO 策略模式+工厂模式优化
		//1. 根据商品类型从工厂中返回具体策略实现类对象
		DeliveryStrategy strategy = deliveryStrategyFactory.getStrategy(userPaidRecordVo.getItemType());
		//2. 调用策略实现类对象进行虚拟物品发货逻辑
		strategy.delivery(userPaidRecordVo);
	}
}
